package com.leonardo.persistenceme.model.persistancehandlers;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Vector;

import MAR.Core.Field;
import MAR.Core.Reflector;

import com.leonardo.persistenceme.Class2Handler;
import com.leonardo.persistenceme.PersistGraphController;
import com.leonardo.persistenceme.exceptions.DatabaseOpenException;
import com.leonardo.persistenceme.exceptions.DatabaseOperationException;
import com.leonardo.persistenceme.model.Entity;
import com.leonardo.persistenceme.model.PersistenceManager;
import com.leonardo.persistenceme.model.PersistenceManagerFactory;
import com.leonardo.persistenceme.utils.VectorUtils;

public class DefaultPersistenceHandler extends PersistenceHandler {
	
	private static final String NULL_CTRL = "!#NULL_CONTROL#!";
	
	private void persistAsEntity(Entity entity, DataOutputStream stream) throws IOException {
		try {
			PersistenceManager pm = PersistenceManagerFactory.getPersistenceManager(DB_NAME);
			PersistenceHandler handler = Class2Handler.getHandlerOrDefault(entity.getClass());
			pm.update(entity, handler);
			stream.writeInt(entity.getKey().intValue());
		} catch(DatabaseOpenException e) { // Failure! Set null
			stream.writeInt(0);
		}
	}
	
	public void persist(Object obj, DataOutputStream stream) throws IOException {		
		if(obj == null) {
			stream.writeUTF(NULL_CTRL);
			return;
		}
		
		{/*System.out.println(obj.getClass().getName());*/}
		stream.writeUTF(obj.getClass().getName());
		
		if(!(obj instanceof Reflector))
			throw new IllegalArgumentException();
		
		if(obj instanceof Entity) { //Become foreign key
			if(!root) {
				persistAsEntity((Entity) obj, stream); 
				return;
			}
			stream.writeInt(((Entity) obj).getKey().intValue());
		}
		
		Reflector reflector = (Reflector) obj;
		Vector fields;
		try {
			fields = reflector.getFields();
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e.getMessage());
		}
		VectorUtils.sortFieldsVector(fields);
		
		for(int i = 0; i < fields.size(); i++) {
			Field field = (Field) fields.elementAt(i);
			Object value = field.get(reflector);
			try {		
				PersistenceHandler handler = Class2Handler.getHandlerOrDefault(field.getType());
				handler.setDB_NAME(DB_NAME);
				handler.persist(value, stream);
			} catch(IllegalArgumentException e) {
				{/*System.out.println(field.getName() + " cannot be persisted");*/}
			}
		}
	}
	
	private Entity recoverAsEntity(Class clazz, int key) {
		Entity recovered = PersistGraphController.alreadyRecovered(new Integer(key));
		if(recovered != null)
			return recovered;
		try {
			PersistenceManager pm = PersistenceManagerFactory.getPersistenceManager(DB_NAME);
			PersistenceHandler handler = Class2Handler.getHandlerOrDefault(clazz);
			handler.setDB_NAME(DB_NAME);
			return pm.getByKey(new Integer(key), handler);
		} catch (DatabaseOpenException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public Object recover(DataInputStream stream) throws IOException {
		String className = stream.readUTF();
		if(className.equals(NULL_CTRL))
			return null;
		
		{/*System.out.println(className);*/}
		Reflector obj;
		try {
			Object o = Class.forName(className).newInstance();
			if(o instanceof Entity) {
				int key = stream.readInt();
				if(!root)
					return recoverAsEntity(o.getClass(), key);
				((Entity) o).setKey(new Integer(key));
				PersistGraphController.addRecovered((Entity) o);
			}
			obj = (Reflector) o;
		} catch (InstantiationException e) {
			throw new RuntimeException("Entity should have default contructor");
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Entity should have default contructor");
		} catch (ClassNotFoundException e) {
			throw new DatabaseOperationException("Try to recover unknow " +
					"class from database");
		} catch(ClassCastException e) {
			return null; //Object not supported for save
		}
		
		Vector fields;
		try {
			fields = obj.getFields();
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e.getMessage());
		}
		VectorUtils.sortFieldsVector(fields);
		for(int i = 0; i < fields.size(); i++) {
			Field field = (Field) fields.elementAt(i);
			PersistenceHandler handler = Class2Handler.getHandlerOrDefault(field.getType()); 
			handler.setDB_NAME(DB_NAME);
			Object value = handler.recover(stream);
			if(value != null)
				field.set(obj, value);
			else
				{/*System.out.println(field.getName() + " was set to default");*/}
		}
		if(obj instanceof Entity)
			PersistGraphController.endRecover();
		return obj;
	}

}
